/**
 * Copyright 2007-2008 Vidar Svansson
 *     
 * This file is part of JAdapter.
 * 
 * JAdapter is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * JAdapter is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with JAdapter.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.jadapter.weavers;

import org.jadapter.func.Filter;
import org.jadapter.registry.InterfaceProvider;
import org.jadapter.registry.InterfaceProviderRegistry;
import org.jadapter.registry.TransformerRegistry;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Weaver that weaves the target object with a {@link TransformerRegistry}.
 * The resulting object is a proxy of all interfaces declared by the target combined
 * with all interfaces reachable from it through transformation.
 * 
 * <br></br><br></br>
 * <strong>Note:</strong>
 * Only interface level proxies are currently supported. 
 * The weaving should be performed <em>after</em> the registry has been fully
 * initialized (all adapters registered).
 * 
 * <br></br><br></br>
 * Subclasses can override the {@link Filter} implementation to decided if an 
 * object should be weaved or not.
 * 
 * 
 * @author Vidar Svansson
 * @since 0.2
 */
public class RegistryWeaver implements Weaver, Filter<Class<?>> {


    private TransformerRegistry registry;
    private InterfaceProvider interfaceProvider;

    
    public RegistryWeaver(TransformerRegistry registry, InterfaceProvider interfaceProvider) {
        this.registry = registry;
        this.interfaceProvider = interfaceProvider;
    }
    
    public RegistryWeaver(InterfaceProviderRegistry registry){
    	this(registry, registry);
    }
    
    
    public <T> T weave(T t) {

        Class<?> klass = t.getClass();
        if (filter(klass)) {
            Class<?>[] beanInterfaces = klass.getInterfaces();
            
            if (beanInterfaces.length == 0)
                return t; // can't do anything with plain classes

            Class<?>[] interfaces = interfaceProvider.interfaces(beanInterfaces);

            if (interfaces.length > 1) {
                // only do this when there are adapters registered for the bean
                // type
                return (T) Proxy.newProxyInstance(
                        t.getClass().getClassLoader(), interfaces,
                        new AdapterInvocationHandler(registry, t));
            }
        }
        return t;
    }    

    private final class AdapterInvocationHandler implements InvocationHandler {
        private TransformerRegistry registry;
        private Object internal;

        public AdapterInvocationHandler(TransformerRegistry registry, Object internal) {
            this.registry = registry;
            this.internal = internal;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
        	
        	Class<?> owner = method.getDeclaringClass();
        	
        	if(owner.isInstance(internal))        	
        		return method.invoke(internal, args);
        	
        	Object transformed = registry.transform(internal, owner);
        	return method.invoke(transformed, args);
        	
        }

    }

    /** Override this to make custom filtered weavers */
    public boolean filter(Class<?> a) {
        return true;
    }

}
